"""Test the Airobot climate platform."""

from datetime import timedelta
from unittest.mock import AsyncMock

from freezegun.api import FrozenDateTimeFactory
from pyairobotrest.const import MODE_AWAY, MODE_HOME
from pyairobotrest.exceptions import AirobotConnectionError, AirobotError
from pyairobotrest.models import ThermostatSettings, ThermostatStatus
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.climate import (
    ATTR_PRESET_MODE,
    ATTR_TEMPERATURE,
    DOMAIN as CLIMATE_DOMAIN,
    SERVICE_SET_PRESET_MODE,
    SERVICE_SET_TEMPERATURE,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
import homeassistant.helpers.entity_registry as er

from tests.common import MockConfigEntry, snapshot_platform


@pytest.fixture
def platforms() -> list[Platform]:
    """Fixture to specify platforms to test."""
    return [Platform.CLIMATE]


@pytest.mark.usefixtures("init_integration")
async def test_climate_entities(
    hass: HomeAssistant,
    snapshot: SnapshotAssertion,
    entity_registry: er.EntityRegistry,
    mock_config_entry: MockConfigEntry,
    platforms: list[Platform],
) -> None:
    """Test climate entities."""
    await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)


@pytest.mark.parametrize(
    ("mode", "temperature", "method"),
    [
        (1, 24.0, "set_home_temperature"),  # Home mode
        (0, 18.0, "set_away_temperature"),  # Away mode
    ],
)
async def test_climate_set_temperature(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    mock_settings: ThermostatSettings,
    mock_config_entry: MockConfigEntry,
    mode: int,
    temperature: float,
    method: str,
) -> None:
    """Test setting temperature in different modes."""
    # Set device mode
    mock_settings.mode = mode

    mock_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    await hass.services.async_call(
        CLIMATE_DOMAIN,
        SERVICE_SET_TEMPERATURE,
        {
            ATTR_ENTITY_ID: "climate.test_thermostat",
            ATTR_TEMPERATURE: temperature,
        },
        blocking=True,
    )

    getattr(mock_airobot_client, method).assert_called_once_with(temperature)


@pytest.mark.usefixtures("init_integration")
async def test_climate_set_temperature_error(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
) -> None:
    """Test error handling when setting temperature fails."""
    mock_airobot_client.set_home_temperature.side_effect = AirobotError("Device error")

    with pytest.raises(
        ServiceValidationError, match="Failed to set temperature"
    ) as exc_info:
        await hass.services.async_call(
            CLIMATE_DOMAIN,
            SERVICE_SET_TEMPERATURE,
            {
                ATTR_ENTITY_ID: "climate.test_thermostat",
                ATTR_TEMPERATURE: 24.0,
            },
            blocking=True,
        )

    assert exc_info.value.translation_domain == "airobot"
    assert exc_info.value.translation_key == "set_temperature_failed"
    assert exc_info.value.translation_placeholders == {"temperature": "24.0"}


@pytest.mark.parametrize(
    ("preset_mode", "method", "arg"),
    [
        ("home", "set_mode", MODE_HOME),
        ("away", "set_mode", MODE_AWAY),
        ("boost", "set_boost_mode", True),
    ],
)
@pytest.mark.usefixtures("init_integration")
async def test_climate_set_preset_mode(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    preset_mode: str,
    method: str,
    arg: int | bool,
) -> None:
    """Test setting different preset modes."""
    await hass.services.async_call(
        CLIMATE_DOMAIN,
        SERVICE_SET_PRESET_MODE,
        {
            ATTR_ENTITY_ID: "climate.test_thermostat",
            ATTR_PRESET_MODE: preset_mode,
        },
        blocking=True,
    )

    getattr(mock_airobot_client, method).assert_called_once_with(arg)


async def test_climate_set_preset_mode_from_boost_to_home(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    mock_settings: ThermostatSettings,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test disabling boost when switching to home mode."""
    # Set boost mode enabled
    mock_settings.setting_flags.boost_enabled = True

    mock_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    await hass.services.async_call(
        CLIMATE_DOMAIN,
        SERVICE_SET_PRESET_MODE,
        {
            ATTR_ENTITY_ID: "climate.test_thermostat",
            ATTR_PRESET_MODE: "home",
        },
        blocking=True,
    )

    # Should disable boost first, then set mode to home
    mock_airobot_client.set_boost_mode.assert_called_once_with(False)
    mock_airobot_client.set_mode.assert_called_once_with(MODE_HOME)


@pytest.mark.usefixtures("init_integration")
async def test_climate_set_preset_mode_error(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
) -> None:
    """Test error handling when setting preset mode fails."""
    mock_airobot_client.set_boost_mode.side_effect = AirobotError("Device error")

    with pytest.raises(
        ServiceValidationError, match="Failed to set preset mode"
    ) as exc_info:
        await hass.services.async_call(
            CLIMATE_DOMAIN,
            SERVICE_SET_PRESET_MODE,
            {
                ATTR_ENTITY_ID: "climate.test_thermostat",
                ATTR_PRESET_MODE: "boost",
            },
            blocking=True,
        )

    assert exc_info.value.translation_domain == "airobot"
    assert exc_info.value.translation_key == "set_preset_mode_failed"
    assert exc_info.value.translation_placeholders == {"preset_mode": "boost"}


async def test_climate_heating_state(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    mock_status: ThermostatStatus,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test climate entity shows heating action when heating."""
    # Set heating on
    mock_status.status_flags.heating_on = True

    mock_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    state = hass.states.get("climate.test_thermostat")
    assert state
    assert state.attributes.get("hvac_action") == "heating"


@pytest.mark.usefixtures("init_integration")
async def test_climate_unavailable_on_update_failure(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test climate entity becomes unavailable when coordinator update fails."""
    # Initially available
    state = hass.states.get("climate.test_thermostat")
    assert state
    assert state.state != "unavailable"

    # Simulate connection error during update
    mock_airobot_client.get_statuses.side_effect = AirobotConnectionError(
        "Connection lost"
    )
    mock_airobot_client.get_settings.side_effect = AirobotConnectionError(
        "Connection lost"
    )

    # Advance time to trigger coordinator update (30 second interval)
    freezer.tick(timedelta(seconds=35))
    await hass.async_block_till_done()

    # Entity should now be unavailable
    state = hass.states.get("climate.test_thermostat")
    assert state
    assert state.state == "unavailable"


@pytest.mark.parametrize(
    ("temp_floor", "temp_air", "expected_temp"),
    [
        (25.0, 22.0, 25.0),  # Floor sensor available - should use floor temp
        (None, 22.0, 22.0),  # Floor sensor not available - should use air temp
    ],
)
async def test_climate_current_temperature(
    hass: HomeAssistant,
    mock_airobot_client: AsyncMock,
    mock_status: ThermostatStatus,
    mock_config_entry: MockConfigEntry,
    temp_floor: float | None,
    temp_air: float,
    expected_temp: float,
) -> None:
    """Test current temperature prioritizes floor sensor when available."""
    mock_status.temp_floor = temp_floor
    mock_status.temp_air = temp_air

    mock_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    state = hass.states.get("climate.test_thermostat")
    assert state
    assert state.attributes.get("current_temperature") == expected_temp
